package net.sudot.chess.config;

import net.sudot.chess.business.model.Admin;
import net.sudot.chess.business.model.Customer;
import net.sudot.chess.business.service.AdminService;
import net.sudot.chess.business.service.CustomerService;
import net.sudot.chess.constant.RoleConstants;
import net.sudot.chess.security.Pbkdf2CredentialsMatcher;
import net.sudot.chess.web.advice.GlobalExceptionHandler;
import net.sudot.commons.security.AuthenticationFilter;
import net.sudot.commons.security.AuthorizingRealm;
import net.sudot.commons.security.FirstSuccessfulStrategy;
import net.sudot.commons.security.JwtAuthenticationFilter;
import net.sudot.commons.security.JwtUserAuthenticationToken;
import net.sudot.commons.security.SocialUserAuthenticationToken;
import net.sudot.commons.security.SocialUserAuthorizingRealm;
import net.sudot.commons.security.UserAuthenticationToken;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.pam.ModularRealmAuthenticator;
import org.apache.shiro.cache.MemoryConstrainedCacheManager;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.realm.Realm;
import org.apache.shiro.spring.security.interceptor.AuthorizationAttributeSourceAdvisor;
import org.apache.shiro.spring.web.ShiroFilterFactoryBean;
import org.apache.shiro.web.mgt.DefaultWebSecurityManager;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.Resource;
import javax.servlet.Filter;
import java.util.List;
import java.util.Map;

/**
 * shiro配置
 *
 * @author tangjialin on 2018-06-01.
 */
@Configuration
public class ShiroConfiguration {
    @Resource
    private SystemProperties systemProperties;

    @Bean
    public Pbkdf2CredentialsMatcher credentialsMatcher() {
        return new Pbkdf2CredentialsMatcher();
    }

    @Bean
    public Realm authorizingRealm(CustomerService userService) {
        AuthorizingRealm authorizingRealm = new AuthorizingRealm(credentialsMatcher(), userService);
        authorizingRealm.setAuthenticationTokenClass(UserAuthenticationToken.class);
        return authorizingRealm;
    }

    @Bean
    public Realm jwtAuthorizingRealm(AdminService adminService) {
        AuthorizingRealm realm = new AuthorizingRealm(credentialsMatcher(), adminService);
        realm.setAuthenticationTokenClass(JwtUserAuthenticationToken.class);
        return realm;
    }

    @Bean
    public Realm socialUserAuthorizingRealm(CustomerService userService) {
        SocialUserAuthorizingRealm realm = new SocialUserAuthorizingRealm(credentialsMatcher(), userService);
        realm.setAuthenticationTokenClass(SocialUserAuthenticationToken.class);
        return realm;
    }

    @Bean
    public SecurityManager securityManager(List<Realm> realms) {
        DefaultWebSecurityManager securityManager = new DefaultWebSecurityManager();
        ModularRealmAuthenticator authenticator = new ModularRealmAuthenticator();
        authenticator.setAuthenticationStrategy(new FirstSuccessfulStrategy());
        securityManager.setAuthenticator(authenticator);
        securityManager.setRealms(realms);
        securityManager.setCacheManager(new MemoryConstrainedCacheManager());
        SecurityUtils.setSecurityManager(securityManager);
        return securityManager;
    }

    @Bean
    public AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor(SecurityManager securityManager) {
        AuthorizationAttributeSourceAdvisor authorizationAttributeSourceAdvisor = new AuthorizationAttributeSourceAdvisor();
        authorizationAttributeSourceAdvisor.setSecurityManager(securityManager);
        return authorizationAttributeSourceAdvisor;
    }

    @Bean
    public ShiroFilterFactoryBean shiroFilterFactoryBean(SecurityManager securityManager, ApplicationEventPublisher applicationEventPublisher) {
        ShiroFilterFactoryBean shiroFilterFactoryBean = new ShiroFilterFactoryBean();
        shiroFilterFactoryBean.setSecurityManager(securityManager);
        shiroFilterFactoryBean.setUnauthorizedUrl(GlobalExceptionHandler.ERROR_URI);

        Map<String, String> filterChainDefinitionMap = shiroFilterFactoryBean.getFilterChainDefinitionMap();
        Map<String, Filter> filters = shiroFilterFactoryBean.getFilters();

        filterChainDefinitionMap.put("/favicon.ico", "anon");
        filterChainDefinitionMap.put("/resources/**", "anon");
        filterChainDefinitionMap.put("/static/**", "anon");
        filterChainDefinitionMap.put("/public/**", "anon");

        AuthenticationFilter webAuthcFilter = new AuthenticationFilter(Customer.class)
                .setApplicationEventPublisher(applicationEventPublisher);
        // 配置了LoginUrl之后,此LoginUrl对于的权限易贷要和对应的AuthenticationFilter权限一致,否则不会调用AuthenticationFilter#onLoginSuccess方法
        webAuthcFilter.setLoginUrl("/login");
        webAuthcFilter.setSuccessUrl("/");
        webAuthcFilter.postConstruct();
        filters.put("webAuthc", webAuthcFilter);

//        // 有logout,权限配置,就不用额外实现logout方法了,直接调用其对应URL即可退出登录
//        filterChainDefinitionMap.put("/logout", "logout");
        filterChainDefinitionMap.put("/", "anon");
        filterChainDefinitionMap.put("/MP_verify_*.txt", "anon");
        filterChainDefinitionMap.put("/register", "anon");
        filterChainDefinitionMap.put("/help", "anon");
        filterChainDefinitionMap.put("/qrcode/**", "anon");
        filterChainDefinitionMap.put("/wechat/route", "anon");
        filterChainDefinitionMap.put("/wechat/server", "anon");
        filterChainDefinitionMap.put("/wechat-bot/**", "anon");
        filterChainDefinitionMap.put("/game/detail/**", "webAuthc");
        filterChainDefinitionMap.put("/game/**", "anon");
        filterChainDefinitionMap.put("/error/**", "anon");
        filterChainDefinitionMap.put("/payment/after/**", "anon");
        filterChainDefinitionMap.put("/gamemaster/**", String.format("webAuthc,roles[%s]", RoleConstants.GAME_MASTER));

        AuthenticationFilter adminAuthFilter = new JwtAuthenticationFilter(Admin.class, systemProperties.getJwtSecret())
                .setApplicationEventPublisher(applicationEventPublisher);
        // 配置了LoginUrl之后,此LoginUrl对于的权限易贷要和对应的AuthenticationFilter权限一致,否则不会调用AuthenticationFilter#onLoginSuccess方法
        adminAuthFilter.setLoginUrl("/admin/login");
        adminAuthFilter.setSuccessUrl("/admin/index");
        adminAuthFilter.postConstruct();
        filters.put("adminAuthc", adminAuthFilter);

        filterChainDefinitionMap.put("/admin/wechat-bot/qrcode", "anon");
        filterChainDefinitionMap.put("/admin/**", "adminAuthc");

        filterChainDefinitionMap.put("/**", "webAuthc");
        return shiroFilterFactoryBean;
    }

}